home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / lib / tk2.3 / dist / tk3d.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-06-15  |  27.5 KB  |  994 lines

  1. /* 
  2.  * tk3D.c --
  3.  *
  4.  *    This module provides procedures to draw borders in
  5.  *    the three-dimensional Motif style.
  6.  *
  7.  * Copyright 1990 Regents of the University of California.
  8.  * Permission to use, copy, modify, and distribute this
  9.  * software and its documentation for any purpose and without
  10.  * fee is hereby granted, provided that the above copyright
  11.  * notice appear in all copies.  The University of California
  12.  * makes no representations about the suitability of this
  13.  * software for any purpose.  It is provided "as is" without
  14.  * express or implied warranty.
  15.  */
  16.  
  17. #ifndef lint
  18. static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tk3d.c,v 1.30 92/06/15 14:28:18 ouster Exp $ SPRITE (Berkeley)";
  19. #endif
  20.  
  21. #include "tkConfig.h"
  22. #include "tk.h"
  23.  
  24. /*
  25.  * One of the following data structures is allocated for
  26.  * each 3-D border currently in use.  Structures of this
  27.  * type are indexed by borderTable, so that a single
  28.  * structure can be shared for several uses.
  29.  */
  30.  
  31. typedef struct {
  32.     Display *display;        /* Display for which the resources
  33.                  * below are allocated. */
  34.     int refCount;        /* Number of different users of
  35.                  * this border.  */
  36.     XColor *bgColorPtr;        /* Background color (intensity
  37.                  * between lightColorPtr and
  38.                  * darkColorPtr). */
  39.     XColor *lightColorPtr;    /* Color used for lighter areas of
  40.                  * border (must free this when
  41.                  * deleting structure). */
  42.     XColor *darkColorPtr;    /* Color for darker areas (must
  43.                  * free when deleting structure). */
  44.     Pixmap shadow;        /* Stipple pattern to use for drawing
  45.                  * lighter-shadow-ed areas.  Only used on
  46.                  * monochrome displays;  on color displays
  47.                  * this is None. */
  48.     GC lightGC;            /* Used to draw lighter parts of
  49.                  * the border. */
  50.     GC darkGC;            /* Used to draw darker parts of the
  51.                  * border. */
  52.     GC bgGC;            /* Used (if necessary) to draw areas in
  53.                  * the background color. */
  54.     Tcl_HashEntry *hashPtr;    /* Entry in borderTable (needed in
  55.                  * order to delete structure). */
  56. } Border;
  57.  
  58. /*
  59.  * Hash table to map from a border's values (color, etc.) to a
  60.  * Border structure for those values.
  61.  */
  62.  
  63. static Tcl_HashTable borderTable;
  64. typedef struct {
  65.     Tk_Uid colorName;        /* Color for border. */
  66.     Colormap colormap;        /* Colormap used for allocating border
  67.                  * colors. */
  68.     Screen *screen;        /* Screen on which border will be drawn. */
  69. } BorderKey;
  70.  
  71. /*
  72.  * Maximum intensity for a color:
  73.  */
  74.  
  75. #define MAX_INTENSITY 65535
  76.  
  77.  
  78. static int initialized = 0;    /* 0 means static structures haven't
  79.                  * been initialized yet. */
  80.  
  81. /*
  82.  * Forward declarations for procedures defined in this file:
  83.  */
  84.  
  85. static void        BorderInit _ANSI_ARGS_((void));
  86. static int        Intersect _ANSI_ARGS_((XPoint *a1Ptr, XPoint *a2Ptr,
  87.                 XPoint *b1Ptr, XPoint *b2Ptr, XPoint *iPtr));
  88. static void        ShiftLine _ANSI_ARGS_((XPoint *p1Ptr, XPoint *p2Ptr,
  89.                 int distance, XPoint *p3Ptr));
  90.  
  91. /*
  92.  *--------------------------------------------------------------
  93.  *
  94.  * Tk_Get3DBorder --
  95.  *
  96.  *    Create a data structure for displaying a 3-D border.
  97.  *
  98.  * Results:
  99.  *    The return value is a token for a data structure
  100.  *    describing a 3-D border.  This token may be passed
  101.  *    to Tk_Draw3DRectangle and Tk_Free3DBorder.  If an
  102.  *    error prevented the border from being created then
  103.  *    NULL is returned and an error message will be left
  104.  *    in interp->result.
  105.  *
  106.  * Side effects:
  107.  *    Data structures, graphics contexts, etc. are allocated.
  108.  *    It is the caller's responsibility to eventually call
  109.  *    Tk_Free3DBorder to release the resources.
  110.  *
  111.  *--------------------------------------------------------------
  112.  */
  113.  
  114. Tk_3DBorder
  115. Tk_Get3DBorder(interp, tkwin, colormap, colorName)
  116.     Tcl_Interp *interp;        /* Place to store an error message. */
  117.     Tk_Window tkwin;        /* Token for window in which
  118.                  * border will be drawn. */
  119.     Colormap colormap;        /* Colormap to use for allocating border
  120.                  * colors.  None means use default colormap
  121.                  * for screen. */
  122.     Tk_Uid colorName;        /* String giving name of color
  123.                  * for window background. */
  124. {
  125.     BorderKey key;
  126.     Tcl_HashEntry *hashPtr;
  127.     register Border *borderPtr;
  128.     int new;
  129.     unsigned long light, dark;
  130.     XGCValues gcValues;
  131.     unsigned long mask;
  132.  
  133.     if (!initialized) {
  134.     BorderInit();
  135.     }
  136.  
  137.     /*
  138.      * First, check to see if there's already a border that will work
  139.      * for this request.
  140.      */
  141.  
  142.     key.colorName = colorName;
  143.     if (colormap == None) {
  144.     colormap = DefaultColormapOfScreen(Tk_Screen(tkwin));
  145.     }
  146.     key.colormap = colormap;
  147.     key.screen = Tk_Screen(tkwin);
  148.  
  149.     hashPtr = Tcl_CreateHashEntry(&borderTable, (char *) &key, &new);
  150.     if (!new) {
  151.     borderPtr = (Border *) Tcl_GetHashValue(hashPtr);
  152.     borderPtr->refCount++;
  153.     } else {
  154.  
  155.     /*
  156.      * No satisfactory border exists yet.  Initialize a new one.
  157.      */
  158.     
  159.     borderPtr = (Border *) ckalloc(sizeof(Border));
  160.     borderPtr->display = Tk_Display(tkwin);
  161.     borderPtr->refCount = 1;
  162.     borderPtr->bgColorPtr = NULL;
  163.     borderPtr->lightColorPtr = NULL;
  164.     borderPtr->darkColorPtr = NULL;
  165.     borderPtr->shadow = None;
  166.     borderPtr->lightGC = None;
  167.     borderPtr->darkGC = None;
  168.     borderPtr->bgGC = None;
  169.     borderPtr->hashPtr = hashPtr;
  170.     Tcl_SetHashValue(hashPtr, borderPtr);
  171.     
  172.     /*
  173.      * Figure out what colors and GC's to use for the light
  174.      * and dark areas and set up the graphics contexts.
  175.      * Monochrome displays get handled differently than
  176.      * color displays.
  177.      */
  178.     
  179.     borderPtr->bgColorPtr = Tk_GetColor(interp, tkwin,
  180.         key.colormap, colorName);
  181.     if (borderPtr->bgColorPtr == NULL) {
  182.         goto error;
  183.     }
  184.     if (DefaultDepthOfScreen(Tk_Screen(tkwin)) == 1) {
  185.         /*
  186.          * Monochrome display.
  187.          */
  188.     
  189.         light = borderPtr->bgColorPtr->pixel;
  190.         if (light == WhitePixelOfScreen(Tk_Screen(tkwin))) {
  191.         dark = BlackPixelOfScreen(Tk_Screen(tkwin));
  192.         } else {
  193.         dark = WhitePixelOfScreen(Tk_Screen(tkwin));
  194.         }
  195.         borderPtr->shadow = Tk_GetBitmap(interp, tkwin,
  196.             Tk_GetUid("gray50"));
  197.         if (borderPtr->shadow == None) {
  198.         goto error;
  199.         }
  200.     } else {
  201.         XColor lightColor, darkColor;
  202.         int tmp;
  203.  
  204.         /*
  205.          * Color display.  Compute the colors for the illuminated
  206.          * and shaded portions of the border.
  207.          */
  208.     
  209.         tmp = (14*borderPtr->bgColorPtr->red)/10;
  210.         if (tmp > MAX_INTENSITY) {
  211.         tmp = MAX_INTENSITY;
  212.         }
  213.         lightColor.red = tmp;
  214.         tmp = (14*borderPtr->bgColorPtr->green)/10;
  215.         if (tmp > MAX_INTENSITY) {
  216.         tmp = MAX_INTENSITY;
  217.         }
  218.         lightColor.green = tmp;
  219.         tmp = (14*borderPtr->bgColorPtr->blue)/10;
  220.         if (tmp > MAX_INTENSITY) {
  221.         tmp = MAX_INTENSITY;
  222.         }
  223.         lightColor.blue = tmp;
  224.         darkColor.red = (60*borderPtr->bgColorPtr->red)/100;
  225.         darkColor.green = (60*borderPtr->bgColorPtr->green)/100;
  226.         darkColor.blue = (60*borderPtr->bgColorPtr->blue)/100;
  227.         borderPtr->lightColorPtr = Tk_GetColorByValue(interp, tkwin,
  228.             key.colormap, &lightColor);
  229.         if (borderPtr->lightColorPtr == NULL) {
  230.         goto error;
  231.         }
  232.         borderPtr->darkColorPtr = Tk_GetColorByValue(interp, tkwin,
  233.             key.colormap, &darkColor);
  234.         if (borderPtr->darkColorPtr == NULL) {
  235.         goto error;
  236.         }
  237.         light = borderPtr->lightColorPtr->pixel;
  238.         dark = borderPtr->darkColorPtr->pixel;
  239.     }
  240.     gcValues.foreground = light;
  241.     gcValues.background = dark;
  242.     mask = GCForeground|GCBackground;
  243.     if (borderPtr->shadow != None) {
  244.         gcValues.stipple = borderPtr->shadow;
  245.         gcValues.fill_style = FillOpaqueStippled;
  246.         mask |= GCStipple|GCFillStyle;
  247.     }
  248.     borderPtr->lightGC = Tk_GetGC(tkwin, mask, &gcValues);
  249.     gcValues.foreground = dark;
  250.     gcValues.background = light;
  251.     borderPtr->darkGC = Tk_GetGC(tkwin, GCForeground|GCBackground,
  252.         &gcValues);
  253.     gcValues.foreground = borderPtr->bgColorPtr->pixel;
  254.     borderPtr->bgGC = Tk_GetGC(tkwin, GCForeground, &gcValues);
  255.     }
  256.     return (Tk_3DBorder) borderPtr;
  257.  
  258.     error:
  259.     Tk_Free3DBorder((Tk_3DBorder) borderPtr);
  260.     return NULL;
  261. }
  262.  
  263. /*
  264.  *--------------------------------------------------------------
  265.  *
  266.  * Tk_Draw3DRectangle --
  267.  *
  268.  *    Draw a 3-D border at a given place in a given window.
  269.  *
  270.  * Results:
  271.  *    None.
  272.  *
  273.  * Side effects:
  274.  *    A 3-D border will be drawn in the indicated drawable.
  275.  *    The outside edges of the border will be determined by x,
  276.  *    y, width, and height.  The inside edges of the border
  277.  *    will be determined by the borderWidth argument.
  278.  *
  279.  *--------------------------------------------------------------
  280.  */
  281.  
  282. void
  283. Tk_Draw3DRectangle(display, drawable, border, x, y, width, height,
  284.     borderWidth, relief)
  285.     Display *display;        /* X display in which to draw. */
  286.     Drawable drawable;        /* X window or pixmap in which to draw. */
  287.     Tk_3DBorder border;        /* Token for border to draw. */
  288.     int x, y, width, height;    /* Outside area of region in
  289.                  * which border will be drawn. */
  290.     int borderWidth;        /* Desired width for border, in
  291.                  * pixels. */
  292.     int relief;            /* Should be either TK_RELIEF_RAISED
  293.                  * or TK_RELIEF_SUNKEN;  indicates
  294.                  * position of interior of window relative
  295.                  * to exterior. */
  296. {
  297.     register Border *borderPtr = (Border *) border;
  298.     GC top, bottom;
  299.     XPoint points[7];
  300.  
  301.     if ((width < 2*borderWidth) || (height < 2*borderWidth)) {
  302.     return;
  303.     }
  304.  
  305.     if (relief == TK_RELIEF_RAISED) {
  306.     top = borderPtr->lightGC;
  307.     bottom = borderPtr->darkGC;
  308.     } else if (relief == TK_RELIEF_SUNKEN) {
  309.     top = borderPtr->darkGC;
  310.     bottom = borderPtr->lightGC;
  311.     } else {
  312.     top = bottom = borderPtr->bgGC;
  313.     }
  314.     XFillRectangle(display, drawable, bottom, x, y+height-borderWidth,
  315.  
  316.         (unsigned int) width, (unsigned int) borderWidth);
  317.     XFillRectangle(display, drawable, bottom, x+width-borderWidth, y,
  318.         (unsigned int) borderWidth, (unsigned int) height);
  319.     points[0].x = points[1].x = points[6].x = x;
  320.     points[0].y = points[6].y = y + height;
  321.     points[1].y = points[2].y = y;
  322.     points[2].x = x + width;
  323.     points[3].x = x + width - borderWidth;
  324.     points[3].y = points[4].y = y + borderWidth;
  325.     points[4].x = points[5].x = x + borderWidth;
  326.     points[5].y = y + height - borderWidth;
  327.     XFillPolygon(display, drawable, top, points, 7, Nonconvex,
  328.         CoordModeOrigin);
  329. }
  330.  
  331. /*
  332.  *--------------------------------------------------------------
  333.  *
  334.  * Tk_NameOf3DBorder --
  335.  *
  336.  *    Given a border, return a textual string identifying the
  337.  *    border's color.
  338.  *
  339.  * Results:
  340.  *    The return value is the string that was used to create
  341.  *    the border.
  342.  *
  343.  * Side effects:
  344.  *    None.
  345.  *
  346.  *--------------------------------------------------------------
  347.  */
  348.  
  349. char *
  350. Tk_NameOf3DBorder(border)
  351.     Tk_3DBorder border;        /* Token for border. */
  352. {
  353.     Border *borderPtr = (Border *) border;
  354.  
  355.     return ((BorderKey *) borderPtr->hashPtr->key.words)->colorName;
  356. }
  357.  
  358. /*
  359.  *--------------------------------------------------------------------
  360.  *
  361.  * Tk_3DBorderColor --
  362.  *
  363.  *    Given a 3D border, return the X color used for the "flat"
  364.  *    surfaces.
  365.  *
  366.  * Results:
  367.  *    Returns the color used drawing flat surfaces with the border.
  368.  *
  369.  * Side effects:
  370.  *    None.
  371.  *
  372.  *--------------------------------------------------------------------
  373.  */
  374. XColor *
  375. Tk_3DBorderColor(border)
  376.     Tk_3DBorder border;
  377. {
  378.     return(((Border *) border)->bgColorPtr);
  379. }
  380.  
  381. /*
  382.  *--------------------------------------------------------------
  383.  *
  384.  * Tk_Free3DBorder --
  385.  *
  386.  *    This procedure is called when a 3D border is no longer
  387.  *    needed.  It frees the resources associated with the
  388.  *    border.  After this call, the caller should never again
  389.  *    use the "border" token.
  390.  *
  391.  * Results:
  392.  *    None.
  393.  *
  394.  * Side effects:
  395.  *    Resources are freed.
  396.  *
  397.  *--------------------------------------------------------------
  398.  */
  399.  
  400. void
  401. Tk_Free3DBorder(border)
  402.     Tk_3DBorder border;        /* Token for border to be released. */
  403. {
  404.     register Border *borderPtr = (Border *) border;
  405.  
  406.     borderPtr->refCount--;
  407.     if (borderPtr->refCount == 0) {
  408.     if (borderPtr->bgColorPtr != NULL) {
  409.         Tk_FreeColor(borderPtr->bgColorPtr);
  410.     }
  411.     if (borderPtr->lightColorPtr != NULL) {
  412.         Tk_FreeColor(borderPtr->lightColorPtr);
  413.     }
  414.     if (borderPtr->darkColorPtr != NULL) {
  415.         Tk_FreeColor(borderPtr->darkColorPtr);
  416.     }
  417.     if (borderPtr->shadow != None) {
  418.         Tk_FreeBitmap(borderPtr->shadow);
  419.     }
  420.     if (borderPtr->lightGC != None) {
  421.         Tk_FreeGC(borderPtr->lightGC);
  422.     }
  423.     if (borderPtr->darkGC != None) {
  424.         Tk_FreeGC(borderPtr->darkGC);
  425.     }
  426.     if (borderPtr->bgGC != None) {
  427.         Tk_FreeGC(borderPtr->bgGC);
  428.     }
  429.     Tcl_DeleteHashEntry(borderPtr->hashPtr);
  430.     ckfree((char *) borderPtr);
  431.     }
  432. }
  433.  
  434. /*
  435.  *----------------------------------------------------------------------
  436.  *
  437.  * Tk_SetBackgroundFromBorder --
  438.  *
  439.  *    Change the background of a window to one appropriate for a given
  440.  *    3-D border.
  441.  *
  442.  * Results:
  443.  *    None.
  444.  *
  445.  * Side effects:
  446.  *    Tkwin's background gets modified.
  447.  *
  448.  *----------------------------------------------------------------------
  449.  */
  450.  
  451. void
  452. Tk_SetBackgroundFromBorder(tkwin, border)
  453.     Tk_Window tkwin;        /* Window whose background is to be set. */
  454.     Tk_3DBorder border;        /* Token for border. */
  455. {
  456.     register Border *borderPtr = (Border *) border;
  457.  
  458.     Tk_SetWindowBackground(tkwin, borderPtr->bgColorPtr->pixel);
  459. }
  460.  
  461. /*
  462.  *----------------------------------------------------------------------
  463.  *
  464.  * Tk_GetRelief --
  465.  *
  466.  *    Parse a relief description and return the corresponding
  467.  *    relief value, or an error.
  468.  *
  469.  * Results:
  470.  *    A standard Tcl return value.  If all goes well then
  471.  *    *reliefPtr is filled in with one of the values
  472.  *    TK_RELIEF_RAISED, TK_RELIEF_FLAT, or TK_RELIEF_SUNKEN.
  473.  *
  474.  * Side effects:
  475.  *    None.
  476.  *
  477.  *----------------------------------------------------------------------
  478.  */
  479.  
  480. int
  481. Tk_GetRelief(interp, name, reliefPtr)
  482.     Tcl_Interp *interp;        /* For error messages. */
  483.     char *name;            /* Name of a relief type. */
  484.     int *reliefPtr;        /* Where to store converted relief. */
  485. {
  486.     char c;
  487.     int length;
  488.  
  489.     c = name[0];
  490.     length = strlen(name);
  491.     if ((c == 'f') && (strncmp(name, "flat", length) == 0)) {
  492.     *reliefPtr = TK_RELIEF_FLAT;
  493.     } else if ((c == 'r') && (strncmp(name, "raised", length) == 0)) {
  494.     *reliefPtr = TK_RELIEF_RAISED;
  495.     } else if ((c == 's') && (strncmp(name, "sunken", length) == 0)) {
  496.     *reliefPtr = TK_RELIEF_SUNKEN;
  497.     } else {
  498.     sprintf(interp->result, "bad relief type \"%.50s\":  must be %s",
  499.         name, "flat, raised, or sunken");
  500.     return TCL_ERROR;
  501.     }
  502.     return TCL_OK;
  503. }
  504.  
  505. /*
  506.  *--------------------------------------------------------------
  507.  *
  508.  * Tk_NameOfRelief --
  509.  *
  510.  *    Given a relief value, produce a string describing that
  511.  *    relief value.
  512.  *
  513.  * Results:
  514.  *    The return value is a static string that is equivalent
  515.  *    to relief.
  516.  *
  517.  * Side effects:
  518.  *    None.
  519.  *
  520.  *--------------------------------------------------------------
  521.  */
  522.  
  523. char *
  524. Tk_NameOfRelief(relief)
  525.     int relief;        /* One of TK_RELIEF_FLAT, TK_RELIEF_RAISED,
  526.              * or TK_RELIEF_SUNKEN. */
  527. {
  528.     if (relief == TK_RELIEF_FLAT) {
  529.     return "flat";
  530.     } else if (relief == TK_RELIEF_SUNKEN) {
  531.     return "sunken";
  532.     } else if (relief == TK_RELIEF_RAISED) {
  533.     return "raised";
  534.     } else {
  535.     return "unknown relief";
  536.     }
  537. }
  538.  
  539. /*
  540.  *--------------------------------------------------------------
  541.  *
  542.  * Tk_Draw3DPolygon --
  543.  *
  544.  *    Draw a border with 3-D appearance around the edge of a
  545.  *    given polygon.
  546.  *
  547.  * Results:
  548.  *    None.
  549.  *
  550.  * Side effects:
  551.  *    Information is drawn in "drawable" in the form of a
  552.  *    3-D border borderWidth units width wide on the left
  553.  *    of the trajectory given by pointPtr and numPoints (or
  554.  *    -borderWidth units wide on the right side, if borderWidth
  555.  *    is negative.
  556.  *
  557.  *--------------------------------------------------------------
  558.  */
  559.  
  560. void
  561. Tk_Draw3DPolygon(display, drawable, border, pointPtr, numPoints,
  562.     borderWidth, leftRelief)
  563.     Display *display;        /* X display in which to draw polygon. */
  564.     Drawable drawable;        /* X window or pixmap in which to draw. */
  565.     Tk_3DBorder border;        /* Token for border to draw. */
  566.     XPoint *pointPtr;        /* Array of points describing
  567.                  * polygon.  All points must be
  568.                  * absolute (CoordModeOrigin). */
  569.     int numPoints;        /* Number of points at *pointPtr. */
  570.     int borderWidth;        /* Width of border, measured in
  571.                  * pixels to the left of the polygon's
  572.                  * trajectory.   May be negative. */
  573.     int leftRelief;        /* TK_RELIEF_RAISED or
  574.                  * TK_RELIEF_SUNKEN: indicates how
  575.                  * stuff to left of trajectory looks
  576.                  * relative to stuff on right. */
  577. {
  578.     XPoint poly[4], b1, b2, newB1, newB2;
  579.     XPoint perp, c, shift1, shift2;    /* Used for handling parallel lines. */
  580.     register XPoint *p1Ptr, *p2Ptr;
  581.     Border *borderPtr = (Border *) border;
  582.     GC gc;
  583.     int i, lightOnLeft, dx, dy, parallel, pointsSeen;
  584.  
  585.     /*
  586.      * If the polygon is already closed, drop the last point from it
  587.      * (we'll close it automatically).
  588.      */
  589.  
  590.     p1Ptr = &pointPtr[numPoints-1];
  591.     p2Ptr = &pointPtr[0];
  592.     if ((p1Ptr->x == p2Ptr->x) && (p1Ptr->y == p2Ptr->y)) {
  593.     numPoints--;
  594.     }
  595.  
  596.     /*
  597.      * The loop below is executed once for each vertex in the polgon.
  598.      * At the beginning of each iteration things look like this:
  599.      *
  600.      *          poly[1]       /
  601.      *             *        /
  602.      *             |      /
  603.      *             b1   * poly[0] (pointPtr[i-1])
  604.      *             |    |
  605.      *             |    |
  606.      *             |    |
  607.      *             |    |
  608.      *             |    |
  609.      *             |    | *p1Ptr            *p2Ptr
  610.      *             b2   *--------------------*
  611.      *             |
  612.      *             |
  613.      *             x-------------------------
  614.      *
  615.      * The job of this iteration is to do the following:
  616.      * (a) Compute x (the border corner corresponding to
  617.      *     pointPtr[i]) and put it in poly[2].  As part of
  618.      *       this, compute a new b1 and b2 value for the next
  619.      *       side of the polygon.
  620.      * (b) Put pointPtr[i] into poly[3].
  621.      * (c) Draw the polygon given by poly[0..3].
  622.      * (d) Advance poly[0], poly[1], b1, and b2 for the
  623.      *     next side of the polygon.
  624.      */
  625.  
  626.     /*
  627.      * The above situation doesn't first come into existence until
  628.      * two points have been processed;  the first two points are
  629.      * used to "prime the pump", so some parts of the processing
  630.      * are ommitted for these points.  The variable "pointsSeen"
  631.      * keeps track of the priming process;  it has to be separate
  632.      * from i in order to be able to ignore duplicate points in the
  633.      * polygon.
  634.      */
  635.  
  636.     pointsSeen = 0;
  637.     for (i = -2, p1Ptr = &pointPtr[numPoints-2], p2Ptr = p1Ptr+1;
  638.         i < numPoints; i++, p1Ptr = p2Ptr, p2Ptr++) {
  639.     if ((i == -1) || (i == numPoints-1)) {
  640.         p2Ptr = pointPtr;
  641.     }
  642.     if ((p2Ptr->x == p1Ptr->x) && (p2Ptr->y == p1Ptr->y)) {
  643.         /*
  644.          * Ignore duplicate points (they'd cause core dumps in
  645.          * ShiftLine calls below).
  646.          */
  647.         continue;
  648.     }
  649.     ShiftLine(p1Ptr, p2Ptr, borderWidth, &newB1);
  650.     newB2.x = newB1.x + (p2Ptr->x - p1Ptr->x);
  651.     newB2.y = newB1.y + (p2Ptr->y - p1Ptr->y);
  652.     poly[3] = *p1Ptr;
  653.     parallel = 0;
  654.     if (pointsSeen >= 1) {
  655.         parallel = Intersect(&newB1, &newB2, &b1, &b2, &poly[2]);
  656.  
  657.         /*
  658.          * If two consecutive segments of the polygon are parallel,
  659.          * then things get more complex.  Consider the following
  660.          * diagram:
  661.          *
  662.          * poly[1]
  663.          *    *----b1-----------b2------a
  664.          *                                \
  665.          *                                  \
  666.          *         *---------*----------*    b
  667.          *        poly[0]  *p2Ptr   *p1Ptr  /
  668.          *                                /
  669.          *              --*--------*----c
  670.          *              newB1    newB2
  671.          *
  672.          * Instead of using x and *p1Ptr for poly[2] and poly[3], as
  673.          * in the original diagram, use a and b as above.  Then instead
  674.          * of using x and *p1Ptr for the new poly[0] and poly[1], use
  675.          * b and c as above.
  676.          *
  677.          * Do the computation in three stages:
  678.          * 1. Compute a point "perp" such that the line p1Ptr-perp
  679.          *    is perpendicular to p1Ptr-p2Ptr.
  680.          * 2. Compute the points a and c by intersecting the lines
  681.          *    b1-b2 and newB1-newB2 with p1Ptr-perp.
  682.          * 3. Compute b by shifting p1Ptr-perp to the right and
  683.          *    intersecting it with p1Ptr-p2Ptr.
  684.          */
  685.  
  686.         if (parallel) {
  687.         perp.x = p1Ptr->x + (p2Ptr->y - p1Ptr->y);
  688.         perp.y = p1Ptr->y - (p2Ptr->x - p1Ptr->x);
  689.         (void) Intersect(p1Ptr, &perp, &b1, &b2, &poly[2]);
  690.         (void) Intersect(p1Ptr, &perp, &newB1, &newB2, &c);
  691.         ShiftLine(p1Ptr, &perp, borderWidth, &shift1);
  692.         shift2.x = shift1.x + (perp.x - p1Ptr->x);
  693.         shift2.y = shift1.y + (perp.y - p1Ptr->y);
  694.         (void) Intersect(p1Ptr, p2Ptr, &shift1, &shift2, &poly[3]);
  695.         }
  696.     }
  697.     if (pointsSeen >= 2) {
  698.         dx = poly[3].x - poly[0].x;
  699.         dy = poly[3].y - poly[0].y;
  700.         if (dx > 0) {
  701.         lightOnLeft = (dy <= dx);
  702.         } else {
  703.         lightOnLeft = (dy < dx);
  704.         }
  705.         if (lightOnLeft ^ (leftRelief == TK_RELIEF_RAISED)) {
  706.         gc = borderPtr->lightGC;
  707.         } else {
  708.         gc = borderPtr->darkGC;
  709.         }
  710.         XFillPolygon(display, drawable, gc, poly, 4, Convex,
  711.             CoordModeOrigin);
  712.     }
  713.     b1.x = newB1.x;
  714.     b1.y = newB1.y;
  715.     b2.x = newB2.x;
  716.     b2.y = newB2.y;
  717.     poly[0].x = poly[3].x;
  718.     poly[0].y = poly[3].y;
  719.     if (parallel) {
  720.         poly[1].x = c.x;
  721.         poly[1].y = c.y;
  722.     } else if (pointsSeen >= 1) {
  723.         poly[1].x = poly[2].x;
  724.         poly[1].y = poly[2].y;
  725.     }
  726.     pointsSeen++;
  727.     }
  728. }
  729.  
  730. /*
  731.  *----------------------------------------------------------------------
  732.  *
  733.  * Tk_Fill3DRectangle --
  734.  *
  735.  *    Fill a rectangular area, supplying a 3D border if desired.
  736.  *
  737.  * Results:
  738.  *    None.
  739.  *
  740.  * Side effects:
  741.  *    Information gets drawn on the screen.
  742.  *
  743.  *----------------------------------------------------------------------
  744.  */
  745.  
  746. void
  747. Tk_Fill3DRectangle(display, drawable, border, x, y, width,
  748.     height, borderWidth, relief)
  749.     Display *display;        /* X display in which to draw rectangle. */
  750.     Drawable drawable;        /* X window or pixmap in which to draw. */
  751.     Tk_3DBorder border;        /* Token for border to draw. */
  752.     int x, y, width, height;    /* Outside area of rectangular region. */
  753.     int borderWidth;        /* Desired width for border, in
  754.                  * pixels. Border will be *inside* region. */
  755.     int relief;            /* Indicates 3D effect: TK_RELIEF_FLAT,
  756.                  * TK_RELIEF_RAISED, or TK_RELIEF_SUNKEN. */
  757. {
  758.     register Border *borderPtr = (Border *) border;
  759.  
  760.     XFillRectangle(display, drawable, borderPtr->bgGC,
  761.         x, y, (unsigned int) width, (unsigned int) height);
  762.     if (relief != TK_RELIEF_FLAT) {
  763.     Tk_Draw3DRectangle(display, drawable, border, x, y, width,
  764.         height, borderWidth, relief);
  765.     }
  766. }
  767.  
  768. /*
  769.  *----------------------------------------------------------------------
  770.  *
  771.  * Tk_Fill3DPolygon --
  772.  *
  773.  *    Fill a polygonal area, supplying a 3D border if desired.
  774.  *
  775.  * Results:
  776.  *    None.
  777.  *
  778.  * Side effects:
  779.  *    Information gets drawn on the screen.
  780.  *
  781.  *----------------------------------------------------------------------
  782.  */
  783.  
  784. void
  785. Tk_Fill3DPolygon(display, drawable, border, pointPtr, numPoints,
  786.     borderWidth, leftRelief)
  787.     Display *display;        /* X display in which to draw polygon. */
  788.     Drawable drawable;        /* X window or pixmap in which to draw. */
  789.     Tk_3DBorder border;        /* Token for border to draw. */
  790.     XPoint *pointPtr;        /* Array of points describing
  791.                  * polygon.  All points must be
  792.                  * absolute (CoordModeOrigin). */
  793.     int numPoints;        /* Number of points at *pointPtr. */
  794.     int borderWidth;        /* Width of border, measured in
  795.                  * pixels to the left of the polygon's
  796.                  * trajectory.   May be negative. */
  797.     int leftRelief;            /* Indicates 3D effect of left side of
  798.                  * trajectory relative to right:
  799.                  * TK_RELIEF_FLAT, TK_RELIEF_RAISED,
  800.                  * or TK_RELIEF_SUNKEN. */
  801. {
  802.     register Border *borderPtr = (Border *) border;
  803.  
  804.     XFillPolygon(display, drawable, borderPtr->bgGC,
  805.         pointPtr, numPoints, Complex, CoordModeOrigin);
  806.     if (leftRelief != TK_RELIEF_FLAT) {
  807.     Tk_Draw3DPolygon(display, drawable, border, pointPtr, numPoints,
  808.         borderWidth, leftRelief);
  809.     }
  810. }
  811.  
  812. /*
  813.  *--------------------------------------------------------------
  814.  *
  815.  * BorderInit --
  816.  *
  817.  *    Initialize the structures used for border management.
  818.  *
  819.  * Results:
  820.  *    None.
  821.  *
  822.  * Side effects:
  823.  *    Read the code.
  824.  *
  825.  *-------------------------------------------------------------
  826.  */
  827.  
  828. static void
  829. BorderInit()
  830. {
  831.     initialized = 1;
  832.     Tcl_InitHashTable(&borderTable, sizeof(BorderKey)/sizeof(int));
  833. }
  834.  
  835. /*
  836.  *--------------------------------------------------------------
  837.  *
  838.  * ShiftLine --
  839.  *
  840.  *    Given two points on a line, compute a point on a
  841.  *    new line that is parallel to the given line and
  842.  *    a given distance away from it.
  843.  *
  844.  * Results:
  845.  *    None.
  846.  *
  847.  * Side effects:
  848.  *    None.
  849.  *
  850.  *--------------------------------------------------------------
  851.  */
  852.  
  853. static void
  854. ShiftLine(p1Ptr, p2Ptr, distance, p3Ptr)
  855.     XPoint *p1Ptr;        /* First point on line. */
  856.     XPoint *p2Ptr;        /* Second point on line. */
  857.     int distance;        /* New line is to be this many
  858.                  * units to the left of original
  859.                  * line, when looking from p1 to
  860.                  * p2.  May be negative. */
  861.     XPoint *p3Ptr;        /* Store coords of point on new
  862.                  * line here. */
  863. {
  864.     int dx, dy, dxNeg, dyNeg;
  865.  
  866.     /*
  867.      * The table below is used for a quick approximation in
  868.      * computing the new point.  An index into the table
  869.      * is 128 times the slope of the original line (the slope
  870.      * must always be between 0 and 1).  The value of the table
  871.      * entry is 128 times the amount to displace the new line
  872.      * in y for each unit of perpendicular distance.  In other
  873.      * words, the table maps from the tangent of an angle to
  874.      * the inverse of its cosine.  If the slope of the original
  875.      * line is greater than 1, then the displacement is done in
  876.      * x rather than in y.
  877.      */
  878.  
  879.     static int shiftTable[129];
  880.  
  881.     /*
  882.      * Initialize the table if this is the first time it is
  883.      * used.
  884.      */
  885.  
  886.     if (shiftTable[0] == 0) {
  887.     int i;
  888.     double tangent, cosine;
  889.  
  890.     for (i = 0; i <= 128; i++) {
  891.         tangent = i/128.0;
  892.         cosine = 128/cos(atan(tangent)) + .5;
  893.         shiftTable[i] = cosine;
  894.     }
  895.     }
  896.  
  897.     *p3Ptr = *p1Ptr;
  898.     dx = p2Ptr->x - p1Ptr->x;
  899.     dy = p2Ptr->y - p1Ptr->y;
  900.     if (dy < 0) {
  901.     dyNeg = 1;
  902.     dy = -dy;
  903.     } else {
  904.     dyNeg = 0;
  905.     }
  906.     if (dx < 0) {
  907.     dxNeg = 1;
  908.     dx = -dx;
  909.     } else {
  910.     dxNeg = 0;
  911.     }
  912.     if (dy <= dx) {
  913.     dy = ((distance * shiftTable[(dy<<7)/dx]) + 64) >> 7;
  914.     if (!dxNeg) {
  915.         dy = -dy;
  916.     }
  917.     p3Ptr->y += dy;
  918.     } else {
  919.     dx = ((distance * shiftTable[(dx<<7)/dy]) + 64) >> 7;
  920.     if (dyNeg) {
  921.         dx = -dx;
  922.     }
  923.     p3Ptr->x += dx;
  924.     }
  925. }
  926.  
  927. /*
  928.  *--------------------------------------------------------------
  929.  *
  930.  * Intersect --
  931.  *
  932.  *    Find the intersection point between two lines.
  933.  *
  934.  * Results:
  935.  *    Under normal conditions 0 is returned and the point
  936.  *    at *iPtr is filled in with the intersection between
  937.  *    the two lines.  If the two lines are parallel, then
  938.  *    -1 is returned and *iPtr isn't modified.
  939.  *
  940.  * Side effects:
  941.  *    None.
  942.  *
  943.  *--------------------------------------------------------------
  944.  */
  945.  
  946. static int
  947. Intersect(a1Ptr, a2Ptr, b1Ptr, b2Ptr, iPtr)
  948.     XPoint *a1Ptr;        /* First point of first line. */
  949.     XPoint *a2Ptr;        /* Second point of first line. */
  950.     XPoint *b1Ptr;        /* First point of second line. */
  951.     XPoint *b2Ptr;        /* Second point of second line. */
  952.     XPoint *iPtr;        /* Filled in with intersection point. */
  953. {
  954.     int dxadyb, dxbdya, dxadxb, dyadyb, p, q;
  955.  
  956.     /*
  957.      * The code below is just a straightforward manipulation of two
  958.      * equations of the form y = (x-x1)*(y2-y1)/(x2-x1) + y1 to solve
  959.      * for the x-coordinate of intersection, then the y-coordinate.
  960.      */
  961.  
  962.     dxadyb = (a2Ptr->x - a1Ptr->x)*(b2Ptr->y - b1Ptr->y);
  963.     dxbdya = (b2Ptr->x - b1Ptr->x)*(a2Ptr->y - a1Ptr->y);
  964.     dxadxb = (a2Ptr->x - a1Ptr->x)*(b2Ptr->x - b1Ptr->x);
  965.     dyadyb = (a2Ptr->y - a1Ptr->y)*(b2Ptr->y - b1Ptr->y);
  966.  
  967.     if (dxadyb == dxbdya) {
  968.     return -1;
  969.     }
  970.     p = (a1Ptr->x*dxbdya - b1Ptr->x*dxadyb + (b1Ptr->y - a1Ptr->y)*dxadxb);
  971.     q = dxbdya - dxadyb;
  972.     if (q < 0) {
  973.     p = -p;
  974.     q = -q;
  975.     }
  976.     if (p < 0) {
  977.     iPtr->x = - ((-p + q/2)/q);
  978.     } else {
  979.     iPtr->x = (p + q/2)/q;
  980.     }
  981.     p = (a1Ptr->y*dxadyb - b1Ptr->y*dxbdya + (b1Ptr->x - a1Ptr->x)*dyadyb);
  982.     q = dxadyb - dxbdya;
  983.     if (q < 0) {
  984.     p = -p;
  985.     q = -q;
  986.     }
  987.     if (p < 0) {
  988.     iPtr->y = - ((-p + q/2)/q);
  989.     } else {
  990.     iPtr->y = (p + q/2)/q;
  991.     }
  992.     return 0;
  993. }
  994.